package com.trip.service;

import com.google.common.collect.Lists;
import com.trip.admin.vo.atlas.Product;
import com.trip.admin.vo.atlas.Products;
import com.trip.config.CommonProperties;
import com.trip.dao.tables.pojos.ImportHistoryDomain;
import com.trip.dao.tables.records.*;
import com.trip.exception.ServiceException;
import com.trip.util.IDUtil;
import com.trip.util.StringUtil;
import com.trip.vo.DataSourceEnum;
import com.trip.vo.ImportTaskStatus;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.jooq.DSLContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

import static com.trip.dao.Tables.*;

/**
 * Created by hc24 on 2018/5/5.
 */
@Service
@Transactional
public class DataService {
    private static final Logger logger= LoggerFactory.getLogger(DataService.class);

    public static boolean IS_DEBUG=false;

    public static int DEBUG_PAGE_NUM=1;

    @Autowired
    private CommonProperties commonProperties;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DSLContext dslContext;

    @Autowired
    private PicService picService;

    /** name-code */
    private static Map<String,String> stateName2CodeMap;

    /** tag-code */
    private static Map<String,String> altasTag2codeMap=new HashMap<>();

    private ExecutorService es = Executors.newFixedThreadPool(20);
    private ExecutorService esPic = Executors.newFixedThreadPool(30);

    static{
        //this code can be configed to properties.
        altasTag2codeMap.put("AboriginalCulture","FF");
        altasTag2codeMap.put("Adventure","O");
        altasTag2codeMap.put("Art&Culture","M");
        altasTag2codeMap.put("Beaches&Surf","O");
        altasTag2codeMap.put("Family","RF");
        altasTag2codeMap.put("Food&Wine","FW");
        altasTag2codeMap.put("History&Heritage","LS");
        altasTag2codeMap.put("Luxury","SW");
        altasTag2codeMap.put("MajorEvents","E");
        altasTag2codeMap.put("Nature&Wildlife","PZ");
        altasTag2codeMap.put("Spa&Wellbeing","SW");
        altasTag2codeMap.put("Sports","O");
        altasTag2codeMap.put("SignatureExperiences","E");
        altasTag2codeMap.put("LuxuryLodges","S");
        //altasTag2codeMap.put("YouthOperators",null);
    }

    @Transactional(readOnly = true)
    public List<ImportHistoryDomain> queryHistory(){
        return dslContext.selectFrom(IMPORT_HISTORY).orderBy(IMPORT_HISTORY.IMPORT_START_TIME.desc()).fetchInto(ImportHistoryDomain.class);
    }

    public void manualImportTask(){
        Executors.newSingleThreadExecutor().submit(()-> autoImportTask());
    }

    /**
     * It'll be scheduled at 0:00 every day.
     */
    public void autoImportTask(){
        ImportTaskRecord importTaskRecord = dslContext.selectFrom(IMPORT_TASK).fetchOne();
        if(importTaskRecord!=null&&ImportTaskStatus.RUNNING.name().equals(importTaskRecord.getTaskstatus())){
            throw new ServiceException("Last task is running");
        }

        stateName2CodeMap = dslContext.selectFrom(STATE).fetchStream().collect(Collectors.toMap(StateRecord::getStateName, StateRecord::getCode));

        logger.info("=================Start auto import task=================");
        try {
            importTask();
            importTaskRecord.setTaskstatus(ImportTaskStatus.FINISHED.name());
            logger.info("=================Finish auto import task=================");
        } catch (Exception e) {
            logger.error("=============Fail auto import task==================",e);
            importTaskRecord.setTaskstatus(ImportTaskStatus.ERROR.name());
        }

        int updateNum = dslContext.update(PLACE_AND_EXP.leftJoin(PIC).on(PLACE_AND_EXP.ID.eq(PIC.RELATED_ID)))
                .set(PLACE_AND_EXP.COVER, PIC.ID)
                .where(PLACE_AND_EXP.COVER.isNull())
                .execute();
        logger.info("update {} pae cover",updateNum);


        logger.info("Update import task status");
        importTaskRecord.setId(1);
        importTaskRecord.update();
    }

    private void importTask(){

        ImportHistoryRecord importHistoryRecord = dslContext.newRecord(IMPORT_HISTORY);
        importHistoryRecord.setImportStartTime(LocalDateTime.now());

        int maxPge=1;
        int totalResult=0;
        int successCount=0;

        if(IS_DEBUG){
            maxPge=DEBUG_PAGE_NUM;
        }

        CompletionService<Integer> completionService = new ExecutorCompletionService<>(es);
        for(int i=1;i<=maxPge;i++) {

            String urlFormat = "%sproducts?key=%s&pge=%s&cats=%s&out=json";
            String url = String.format(urlFormat, commonProperties.getAtlas().getUrl(), commonProperties.getAtlas().getKey(), i, commonProperties.getAtlas().getCats());
            logger.info("Get page num {}: {}",i,url);
            Products products = null;
            try {
                products = restTemplate.getForObject(url, Products.class);
            } catch (Exception e) {
                logger.warn("Request list error",e);
                continue;
            }

            if(maxPge==1) {
                totalResult = products.getNumberOfResults();
                logger.info("Total result: "+totalResult);
                if(!IS_DEBUG) {
                    maxPge = totalResult % 10 == 0 ? totalResult / 10 : totalResult / 10 + 1;
                    logger.info("Total Page: " + maxPge);
                }
            }
            List<Products.ProductItem> productList = products.getProducts();


            completionService.submit(()-> requestDetail(productList.stream().map(e->e.getProductId()).collect(Collectors.toList())));
        }

        logger.info("Take result");
        for(int i=1;i<=maxPge;i++){
            try {
                successCount+=completionService.take().get();
            } catch (InterruptedException e) {
               logger.warn("",e);
            } catch (ExecutionException e) {
                logger.warn("",e);
            }
        }

        //insert history record
        logger.info("Insert import history.");
        importHistoryRecord.setImportEndTime(LocalDateTime.now());
        importHistoryRecord.setSuccessCount(successCount);
        importHistoryRecord.setFailedCount(totalResult-successCount);
        if(IS_DEBUG){
            importHistoryRecord.setRemark("MANUAL");
        }else{
            importHistoryRecord.setRemark("AUTO");
        }
        importHistoryRecord.insert();

        //update task status
        //dslContext.update(IMPORT_TASK).set(IMPORT_TASK.TASKSTATUS,ImportTaskStatus.FINISHED.name()).execute();

    }

    private int requestDetail(List<String> productIdList){
        List<PlaceAndExpRecord> placeAndExpRecordList=productIdList.stream().map(e->{
            try{
                return requestDetail(e);
            }catch (Exception ee){
                logger.error("Request detail error: "+e,ee);
            }
            return null;
        }).collect(Collectors.toList());
        if(placeAndExpRecordList.size()>0) {
            placeAndExpRecordList=placeAndExpRecordList.stream().filter(e->e!=null).collect(Collectors.toList());
            try {
                int[] counts = dslContext.batchStore(placeAndExpRecordList).execute();
                int count = 0;
                for (int c : counts) {
                    count += c;
                }
                return count;
            }catch (Exception e){
                logger.warn("store pae error",e);
            }
        }

        return 0;
    }


    private PlaceAndExpRecord requestDetail(String productId){
        String urlFormat="%sproduct?key=%s&productId=%s&out=json";
        String url=String.format(urlFormat,commonProperties.getAtlas().getUrl(),commonProperties.getAtlas().getKey(),productId);
        logger.info(url);
        Product product = restTemplate.getForObject(url, Product.class);
        List<Product.AddressesBean> addressList = product.getAddresses();
        Product.AddressesBean address = null;
        if(!CollectionUtils.isEmpty(addressList))
            address=addressList.get(0);
        List<Product.CommunicationBean> communicationList = product.getCommunication();
        if(communicationList==null)
            communicationList=new ArrayList<>();
        List<Product.MultimediaBean> multimediaList = product.getMultimedia();
        if(multimediaList==null)
            multimediaList=new ArrayList<>();
        List<Product.AttributesBean> attributeList = product.getAttributes();
        if(attributeList==null)
            attributeList=new ArrayList<>();


        PlaceAndExpRecord placeAndExpRecord = dslContext.selectFrom(PLACE_AND_EXP).where(PLACE_AND_EXP.ATLAS_PRODUCT_ID.eq(productId)).fetchOne();
        //New Record
        if(placeAndExpRecord==null) {
            placeAndExpRecord = dslContext.newRecord(PLACE_AND_EXP);
            placeAndExpRecord.setId(IDUtil.next());
            placeAndExpRecord.setType((short) ("EVENT".equals(product.getProductCategoryId())?2:1));

            Set<String> tags=new TreeSet<>();
            for(Product.AttributesBean attributesBean:attributeList){
                if("TAG".equals(attributesBean.getAttributeTypeId())){
                    String tagCode = altasTag2codeMap.get(attributesBean.getAttributeId());
                    if(tagCode!=null)
                        tags.add(tagCode);
                }
            }

            if(!tags.isEmpty())
                placeAndExpRecord.setTags(StringUtils.join(tags,","));

            List<PaeTagsRecord> paeTagsRecordList=new ArrayList<>();
            for(String tag:tags){
                PaeTagsRecord paeTagsRecord=dslContext.newRecord(PAE_TAGS);
                paeTagsRecord.setPaeId(placeAndExpRecord.getId());
                paeTagsRecord.setTagCode(tag);
            }

            if(paeTagsRecordList.size()>0)
                dslContext.batchInsert(paeTagsRecordList).execute();
        }

        //Update Record
        placeAndExpRecord.setTitle(product.getProductName());
        placeAndExpRecord.setDescription(product.getProductDescription());
        if(address!=null){
            placeAndExpRecord.setLat(address.getGeocodeGdaLatitude());
            placeAndExpRecord.setLon(address.getGeocodeGdaLongitude());
            placeAndExpRecord.setState(stateName2CodeMap.get(address.getStateName()));

            placeAndExpRecord.setAddress(getAddressStr(address));
        }


        placeAndExpRecord.setSource(DataSourceEnum.ATLAS.name());
        placeAndExpRecord.setCost(product.getRateFrom());
        placeAndExpRecord.setAtlasProductId(product.getProductId());
        for(Product.CommunicationBean communicationBean:communicationList){
            if(communicationBean.getAttributeIdCommunicationDescription()!=null&&communicationBean.getAttributeIdCommunicationDescription().equals("Email Enquiries")){
                placeAndExpRecord.setEmail(communicationBean.getCommunicationDetail());
            }else if(communicationBean.getAttributeIdCommunicationDescription()!=null&&communicationBean.getAttributeIdCommunicationDescription().equals("Primary Phone")){
                placeAndExpRecord.setPhoneNo(communicationBean.getCommunicationDetail());
            }else if(communicationBean.getAttributeIdCommunicationDescription()!=null&&communicationBean.getAttributeIdCommunicationDescription().equals("URL Enquiries")){
                if(communicationBean.getCommunicationDetail().startsWith("www"))
                    placeAndExpRecord.setWebsite("http://"+communicationBean.getCommunicationDetail());
                else
                    placeAndExpRecord.setWebsite(communicationBean.getCommunicationDetail());
            }
        }

        placeAndExpRecord.setCreatedAt(LocalDateTime.now());

        List<Product.MultimediaBean> finalMultimediaList = multimediaList;
        PlaceAndExpRecord finalPlaceAndExpRecord = placeAndExpRecord;
        esPic.submit(()-> requestPic(finalMultimediaList, finalPlaceAndExpRecord));

        return placeAndExpRecord;
    }

    private String getAddressStr(Product.AddressesBean address) {
        List<String> tempList= Lists.newArrayList();
        if(StringUtils.isNotBlank(address.getAddressLine1())){
            tempList.add(address.getAddressLine1());
        }
        if(StringUtils.isNotBlank(address.getAddressLine2())){
            tempList.add(address.getAddressLine2());
        }
        if(StringUtils.isNotBlank(address.getCityName())){
            tempList.add(address.getCityName());
        }
        if(StringUtils.isNotBlank(address.getAreaName())){
            tempList.add(address.getAreaName());
        }
        return StringUtils.join(tempList,',');
    }

    private void requestPic(List<Product.MultimediaBean> multimediaList, PlaceAndExpRecord placeAndExpRecord) {
        if(placeAndExpRecord==null||placeAndExpRecord.getId()==null)
            logger.warn("pae id is empty");
        if(multimediaList.size()==0) {
            logger.info("{}'s multimedia is empty ");
            return;
        }
        List<PicRecord> picRecordList=new ArrayList<>();
        int seq=1;
        for(Product.MultimediaBean multimedia:multimediaList){
            if(!"IMAGE".equals(multimedia.getAttributeIdMultimediaContent())||multimedia.getWidth()!=800.0||multimedia.getHeight()!=600.0)
                continue;

            String fileName=placeAndExpRecord.getId()+"/"+String.valueOf(UUID.randomUUID())+".jpeg";
            if(dslContext.selectFrom(PIC).where(PIC.ATLAS_ID.eq(multimedia.getMultimediaId())).fetchOne()!=null)
                continue;
            PicRecord picRecord=dslContext.newRecord(PIC);
            picRecord.setId(IDUtil.next());
            picRecord.setWidth(multimedia.getWidth());
            picRecord.setHeight(multimedia.getHeight());
            picRecord.setName(multimedia.getCaption());
            picRecord.setFileName(fileName);
            picRecord.setType(1);
            picRecord.setSeq(seq++);
            picRecord.setRelatedId(placeAndExpRecord.getId());
            picRecord.setAtlasId(multimedia.getMultimediaId());

            logger.info(multimedia.getServerPath());
            byte[] bytes;
            try {
                logger.info(multimedia.getServerPath());
                bytes = restTemplate.getForObject(multimedia.getServerPath(), byte[].class);
            } catch (Exception e) {
                logger.error("Request pic error",e);
                continue;
            }
            try {
                File orgFile=new File( commonProperties.getPicConfig().getPicFolder()+"/pae/"+fileName);
                FileUtils.writeByteArrayToFile(orgFile, bytes);

                picRecordList.add(picRecord);
            }catch (IOException e){
                logger.error("save pic error.",e);
            }
        }

        if(picRecordList.size()>0) {
            dslContext.batchInsert(picRecordList).execute();
        }else{
            logger.info("{}'s image is empty.", placeAndExpRecord.getId());
        }
    }

    public void test(){
//        TagRecord tagRecord = dslContext.newRecord(TAG);
//        tagRecord.setId(15);
//        tagRecord.setTagCode("WC");
//        tagRecord.setTagType(1);
//        tagRecord.setTagName("wocc");
//
//        TagRecord tagRecord2=dslContext.selectFrom(TAG).where(TAG.ID.eq(1)).fetchOne();
//        tagRecord2.setTagName("F&W");
//
//        dslContext.batchStore(tagRecord,tagRecord2).execute();

    }

    public static void main(String[] args) {
        System.out.println(StringUtils.joinWith(",","a","",null,"c"));
    }


}
